#include <stdio.h> 
#include <stdlib.h> 
#include <string.h> 
#include <assert.h>
#include <unistd.h>


#include "contexts.h"
#include "debug.h"
#include "commondefs.h"

#define CTX_ASSERT_WF(c)\
{\
  assert((c) != NULL); \
  assert((c)->ji_no <= CTX_MAX_JI);\
  assert((c)->mi_no <= CTX_MAX_MI);\
}

#define ASSERT_PTR_NN(command)\
{\
  assert((command) != NULL); \
}

#define CTXDIMCHARBUF 100


/** Set the structure */

void
ctx_init(Context *c,int ji_no, int mi_no)
{
  assert(c != NULL);
  assert(ji_no <= CTX_MAX_JI);
  assert(mi_no <= CTX_MAX_MI);

  memset(c,0,sizeof(Context));

  {
   
    int ji,mi;
    char buf[CTXDIMCHARBUF];
    
    /* Set number of jis and mis */
    c->ji_no = ji_no;
    c->mi_no = mi_no;
    
    /* Initialization of ji_names */
    FORALLJIIN(ji,c)
      {
	snprintf(buf,CTXDIMCHARBUF-1,"%d",ji);
	ctx_set_ji_name(c,ji,buf);	
      }
    
    /* Initialization of mi_names */
    FORALLMIIN(mi,c)
      {
	snprintf(buf,CTXDIMCHARBUF-1,"%d",mi);
	ctx_set_mi_name(c,mi,buf);
      }
   }
}

void
ctx_set_orthogonal(Context *c,int ji,int mi)
{
  CTX_ASSERT_WF(c);
  
  if(ji > c->ji_no || mi > c->mi_no)
    exit(EXIT_FAILURE);

  c->leq[ji][mi] = 1;
}

void
ctx_set_name(Context * c,char * name)
{
  strncpy(c->name,name,CTX_NAME_DIM);
}

void
ctx_set_ji_name(Context *c ,int i ,char * n)
{
  int dim =  strlen(n) + 1;

  if(c->ji_names[i])
    free(c->ji_names[i]);

  c->ji_names[i] = malloc(dim);
  assert(c->ji_names[i] != NULL);
  
  strncpy(c->ji_names[i],n,dim);
}

void
ctx_set_mi_name(Context *c ,int i ,char * n)
{
  int dim =  strlen(n) + 1;

  if(c->mi_names[i])
    free(c->mi_names[i]);

  c->mi_names[i] = malloc(dim);
  assert(c->mi_names[i] != NULL);
  
  strncpy(c->mi_names[i],n,dim);
}


/** Delete the structure */
void
ctx_free(Context *c)
{
  int i;

  assert(c != NULL);
  
  FORALLJIIN(i,c)
    if(c->ji_names[i])
      free(c->ji_names[i]);
  
  FORALLMIIN(i,c)
    if(*c->mi_names[i])
      free(c->mi_names[i]);
  
  free(c);
  
}

/** Read the structure */

char *
ctx_name(Context * c)
{
  return c->name;
}

extern char *
ctx_ji_name(Context *c,int i)
{
  return c->ji_names[i];
}

char *
ctx_mi_name(Context *c,int i)
{
  return c->mi_names[i];
}


int 
ctx_no_ji(Context *c)
{
  return c->ji_no;
}

int 
ctx_no_mi(Context *c)
{
  return c->mi_no;
}

int
ctx_orthogonal(Context *c,int ji,int mi)
{
  if(ji <= ctx_no_ji(c) && mi <= ctx_no_mi(c))
    return (c->leq[ji][mi] == 1);
  else
    return 0;
}

/** 
   int 
   ctx_parse : parse function 
   which uses the code ctxctxparse() produced by byson and lex 
   Global variable 
   ctx_tofill 
   (has to be known by yyparse)
*/

Context *ctx_tofill;

void ctx_parse(Context *c,int fd)
{
  extern void ctxctxparse();
  int stdin_save,stdout_save;

  ASSERT_PTR_NN( c );
  ctx_tofill = c;

  if(fd != STDIN_FILENO)
    {
      stdin_save=dup(STDIN_FILENO); 
      close(STDIN_FILENO);
      dup(fd);
      close(fd);
    }
  
  /* Why should we do this hack around ctxctxparse ? */
  printf("%s"," ");  
  stdout_save=dup(STDOUT_FILENO);
  close(STDOUT_FILENO);

  ctxctxparse();
  
  dup(stdout_save);
  close(stdout_save);
  /* End of hack */

  if(fd != STDIN_FILENO)
    {
      close(STDIN_FILENO);
      dup(stdin_save);
      close(stdin_save);
    }
}



/* Initialization, copying, and deletion */


void
ctx_cpy(Context *dest, Context *source)
{
  assert(dest != NULL);
  assert(source != NULL);
  
  memcpy(dest,source,sizeof(Context));
}

void
ctx_del_names(Context *c)
{
  CTX_ASSERT_WF(c);
  
  {
    int ji,mi;

    for(ji=0;ji<=c->ji_no;ji++)
      free(c->ji_names[ji]);

    for(mi=0;mi<=c->mi_no;mi++)
      free(c->mi_names[mi]);
  }
}


/* Operations */

void
ctx_complement(Context *c)
{

  CTX_ASSERT_WF(c);

  {
    register  int ji,mi;
    for(ji= 0; ji < c->ji_no ;ji++)
      for( mi = 0; mi < c->mi_no ; mi++ )
	c->leq[ji][mi] = 1 - c->leq[ji][mi];
  }

}

void 
ctx_rand(Context *c)
{
  CTX_ASSERT_WF(c);
  
  {
    int ji,mi;

    /* Setting the matrix */
    for(ji= 0; ji < c->ji_no ;ji++)
      for( mi = 0; mi < c->mi_no ; mi++ )
	c->leq[ji][mi] = rand()%2; /* Here the only random part !!! */
  }
}


/* Querying a context */
int 
ctx_ji_index(Context *c, char *s)
{
  assert(s != NULL);
  CTX_ASSERT_WF(c);

  {
    register int ji;
  
    for(ji = 0; ji < c->ji_no; ji++)
      {
	if(strcmp(s,c->ji_names[ji]) == 0)
	  return ji;
      }
  }
  return -1;
}

int 
ctx_mi_index(Context *c, char *s)
{

  assert(s != NULL);
  CTX_ASSERT_WF(c);

  {
    register int mi;
  
    for(mi = 0; mi < c->mi_no; mi++)
      {
	if(strcmp(s,c->mi_names[mi]) == 0)
	  return mi;
      }
  }
  return -1;
}

/*  Printing Stuff */

void 
ctx_print_matrix(Context * c)
{
  
  CTX_ASSERT_WF(c);

  {
    int ji,mi;
    
    printf("\t");
    for(mi= 0; mi < c->mi_no;mi ++)
      printf("%s\t", c->mi_names[mi]);
    
    printf("\n");
    
    for(ji= 0; ji < c->ji_no;ji ++)
      {
	printf("%s\t", c->ji_names[ji]);
	for(mi= 0; mi < c->mi_no;mi++)
	  printf("%s\t", (c->leq[ji][mi])?"X":"");
	printf("\n");
      }
    printf("\n");
  }
}

void 
ctx_print(Context *c)
{
  CTX_ASSERT_WF(c);
  
  {
    int ji,mi;
    
    printf("( ");
    printf("%s\n",ctx_name(c));
    
    printf("\t(");
    for(ji= 0; ji < c->ji_no;ji ++)
      printf(" %s ", c->ji_names[ji]);
    printf(")\n");
    
    printf("\t(");
    for(mi= 0; mi < c->mi_no;mi ++)
      printf(" %s ", c->mi_names[mi]);
    printf(")\n");
    
    printf("\t(\n");
    for(ji= 0; ji < c->ji_no;ji ++)
      {
	for(mi= 0; mi < c->mi_no; mi++)
	  {
	    if(c->leq[ji][mi])
	      printf("\t\t( %s %s )\n", 
		     c->ji_names[ji],
		     c->mi_names[mi]);
	  }
      }
    printf("\t)\n");
    
    
    printf(")\n");
  }
}

Subset *
ctx_ji_closure(Context *context, int ji)
{
  int mi;
  
  Subset ss_mi;
  Subset *ss_ji=NEW(Subset);
  
  /** Construct  the image of ji*/
  ss_empty(ctx_no_mi(context),&ss_mi);
  FORALLMIIN(mi,context)
    if(ctx_orthogonal(context,ji,mi))
	ss_addto(&ss_mi,mi);
  
  /** Construct the orthogonal of the image of mi */ 
  ss_total(ctx_no_ji(context),ss_ji);
  SS_FORALLIN(mi,&ss_mi)
    FORALLJIIN(ji,context)
    if(! ctx_orthogonal(context,ji,mi) )
	ss_delfrom(ss_ji,ji) ;

  return ss_ji;
}

Subset *
ctx_mi_closure(Context *context, int mi)
{
  int ji;
  
  Subset ss_ji;
  Subset *ss_mi=NEW(Subset);
  
  /** Construct  the image of mi*/
  ss_empty(ctx_no_ji(context),&ss_ji);
  FORALLJIIN(ji,context)
    if(ctx_orthogonal(context,ji,mi))
	ss_addto(&ss_ji,ji);
  
  /** Construct the orthogonal of the image of ji */ 
  ss_total(ctx_no_mi(context),ss_mi);
  SS_FORALLIN(ji,&ss_ji)
    FORALLMIIN(mi,context)
    if(! ctx_orthogonal(context,mi,ji) )
	ss_delfrom(ss_mi,mi) ;

  return ss_mi;
}

Subset *
ctx_mi_image(Context *context, int mi)
{
  int ji;
  
  Subset *ss_ji=NEW(Subset);
  
  /** Construct  the image of mi*/
  ss_empty(ctx_no_ji(context),ss_ji);
  FORALLJIIN(ji,context)
    if(ctx_orthogonal(context,ji,mi))
	ss_addto(ss_ji,ji);

  return ss_ji;
}

Subset *
ctx_ji_image(Context *context, int ji)
{
  int mi;
  
  Subset *ss_mi=NEW(Subset);
  
  /** Construct  the image of ji*/
  ss_empty(ctx_no_mi(context),ss_mi);
  FORALLJIIN(mi,context)
    if(ctx_orthogonal(context,ji,mi))
	ss_addto(ss_mi,mi);

  return ss_mi;
}

/* returns 1 if ji \jmup mi 
   For this to work, 
   we need the context to be reduced.
*/
int 
ctx_is_jmup(Context *c, int ji,int mi)
{
  int ret = 0;

  Subset *ss_mi;
  Subset *tmp1, *tmp2;

  ss_mi= ctx_ji_closure(c,mi);
  tmp1 = ctx_ji_image(c,ji);
  tmp2 = NEW(Subset);
  ss_empty(ctx_no_mi(c),tmp2);

  ss_complement(tmp1);
  ss_intersection(tmp2,ss_mi,tmp1);

  if(ss_card(tmp2) == 1)
    ret = 1;

  ss_free(ss_mi); ss_free(tmp1); ss_free(tmp2);
    
  return ret;
}
